using DynamicProxy;
using System;
using System.Management;
using System.Reflection;

namespace WMI
{
	public class WmiRoot
	{
		private abstract class BaseHandler : IProxyInvocationHandler
		{
			public abstract object Invoke(object proxy, MethodInfo method, object[] args);

			protected void CheckError(ManagementBaseObject result)
			{
				int num = Convert.ToInt32(result["returnValue"]);
				if (num != 0)
				{
					throw new WmiException((ReturnValue)num);
				}
			}
		}

		private class InstanceHandler : BaseHandler, IWmiObject
		{
			private readonly ManagementObject mo;

			public InstanceHandler(ManagementObject o)
			{
				mo = o;
			}

			public override object Invoke(object proxy, MethodInfo method, object[] args)
			{
				if (method.DeclaringType == typeof(IWmiObject))
				{
					return method.Invoke(this, args);
				}
				if (method.Name.StartsWith("set_"))
				{
					mo[method.Name.Substring(4)] = args[0];
					return null;
				}
				if (method.Name.StartsWith("get_"))
				{
					return mo[method.Name.Substring(4)];
				}
				ParameterInfo[] parameters = method.GetParameters();
				ManagementBaseObject methodParameters = mo.GetMethodParameters(method.Name);
				for (int i = 0; i < args.Length; i++)
				{
					methodParameters[capitalize(parameters[i].Name)] = args[i];
				}
				CheckError(mo.InvokeMethod(method.Name, methodParameters, null));
				return null;
			}

			public void Commit()
			{
				mo.Put();
			}
		}

		private class ClassHandler : BaseHandler
		{
			private readonly ManagementClass mc;

			private readonly string wmiClass;

			public ClassHandler(ManagementClass mc, string wmiClass)
			{
				this.mc = mc;
				this.wmiClass = wmiClass;
			}

			public override object Invoke(object proxy, MethodInfo method, object[] args)
			{
				ParameterInfo[] parameters = method.GetParameters();
				if (method.Name.StartsWith("Select"))
				{
					string text = "SELECT * FROM " + wmiClass + " WHERE ";
					for (int i = 0; i < args.Length; i++)
					{
						if (i != 0)
						{
							text += " AND ";
						}
						object obj = text;
						text = string.Concat(obj, ' ', capitalize(parameters[i].Name), " = '", args[i], "'");
					}
					ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(mc.Scope, new ObjectQuery(text));
					ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
					using (ManagementObjectCollection.ManagementObjectEnumerator managementObjectEnumerator = managementObjectCollection.GetEnumerator())
					{
						if (managementObjectEnumerator.MoveNext())
						{
							ManagementObject o = (ManagementObject)managementObjectEnumerator.Current;
							return ProxyFactory.GetInstance().Create(new InstanceHandler(o), method.ReturnType, isObjInterface: true);
						}
					}
					return null;
				}
				ManagementBaseObject methodParameters = mc.GetMethodParameters(method.Name);
				for (int i = 0; i < args.Length; i++)
				{
					methodParameters[capitalize(parameters[i].Name)] = args[i];
				}
				CheckError(mc.InvokeMethod(method.Name, methodParameters, null));
				return null;
			}
		}

		private readonly ManagementScope scope;

		public WmiRoot()
			: this(null)
		{
		}

		public WmiRoot(string machineName)
		{
			ConnectionOptions options = new ConnectionOptions
			{
				EnablePrivileges = true,
				Impersonation = ImpersonationLevel.Impersonate,
				Authentication = AuthenticationLevel.PacketPrivacy
			};
			scope = new ManagementScope((machineName == null) ? "\\root\\cimv2" : $"\\\\{machineName}\\root\\cimv2", options);
			scope.Connect();
		}

		private static string capitalize(string s)
		{
			return char.ToUpper(s[0]) + s.Substring(1);
		}

		public T GetCollection<T>() where T : IWmiCollection
		{
			WmiClassName wmiClassName = (WmiClassName)typeof(T).GetCustomAttributes(typeof(WmiClassName), inherit: false)[0];
			ObjectGetOptions options = new ObjectGetOptions();
			ManagementPath path = new ManagementPath(wmiClassName.Name);
			ManagementClass mc = new ManagementClass(scope, path, options);
			return (T)ProxyFactory.GetInstance().Create(new ClassHandler(mc, wmiClassName.Name), typeof(T), isObjInterface: true);
		}
	}
}
